home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
TCL1
/
CPOPUPME
/
CPOPUPME.C
< prev
next >
Wrap
Text File
|
1989-08-20
|
16KB
|
579 lines
/*
CPopupMenu - handles a popup menu.
by Dan Podwall (CIS: 70641,145). You may give this code away
freely, but you may not charge for it, other than normal
network download charges.
Displays a standard popup menu title and the currently selected
item. When clicked handles menu selection.
There are two basic modes of operation.
1) When multiSelect is FALSE, only one item may be selected at a
time. Selecting an item other than the currently selected one
deselects the previous one and selects the new one. Selecting
the already selected item does nothing. This is the type of
behaviour you might expect for a font menu, for example.
Think of this like a radio button group. This is the default state
when the popup is created.
2) When multiSelect is TRUE, multiple items may be checked (selected)
simultaneously, as in a style menu. In this case, selections toggle,
i.e. selecting an already selected item deselects (unchecks) it.
This works more like a group of checkboxes.
NOTE: The Draw() method does not currently handle multiple selections,
it only draws the first selected item. That is left as an exercise
for the reader. :)
When useWinFont is TRUE, the title is drawn in the window's current font
rather than the system font. Note that this involves a temporary patch to
TextFont. If that makes you unhappy, then don't use it.
*/
#include "CPopupMenu.h"
#include "CBartender.h"
#include "Commands.h"
#include "StringUtil.h"
#include "CError.h"
extern CBartender *gBartender;
extern CError *gError;
#define kAltCheckMark ((unsigned char) '├') /* ASCII 12 works for Chicago only */
#ifndef _TextFont
#define _TextFont 0xA887
#endif
static Int16 menuFont = 0;
static Int32 realTextFont = 0L;
static void TextFontPatch( void);
/*****************************************************************************/
void CPopupMenu::IPopupMenu( Int16 menuID,CView *anEnclosure, CBureaucrat *aSupervisor,
Int16 aWidth, Int16 aHeight, Int16 aHEncl, Int16 aVEncl,
Int16 isCommandMenu)
{
CPane::IPane( anEnclosure, aSupervisor, aWidth, aHeight, aHEncl, aVEncl,
sizFIXEDSTICKY, sizFIXEDSTICKY);
multiSelect = FALSE;
useWinFont = FALSE;
this->isCommandMenu = isCommandMenu;
IPopupMenuX( menuID);
} /* CPopupMenu::IPopupMenu */
/*****************************************************************************/
void CPopupMenu::IViewTemp(CView *anEnclosure, CBureaucrat *aSupervisor,
Ptr viewData)
{
register tPopupTempPtr template = (tPopupTempPtr) viewData;
inherited::IViewTemp( anEnclosure, aSupervisor, viewData);
useWinFont = template->useWinFont != 0;
autoSelect = template->autoSelect != 0;
multiSelect = template->multiSelect != 0;
isCommandMenu = template->isCommandMenu != 0;
IPopupMenuX( template->menuID);
} /* CPopupMenu::IViewTemp */
/*****************************************************************************/
void CPopupMenu::IPopupMenuX( Int16 menuID)
{
MenuHandle theMenu;
Int16 menuIndex, err;
if (isCommandMenu)
{
/*
has it previously been registered by bartender?
We only want to register it with the bartender 1 time.
*/
menuIndex = gBartender->FindMenuIndex( menuID);
if (menuIndex < 0) /* no it hasn't */
gBartender->AddMenu( menuID, FALSE, -1);
gBartender->SetDimOption( menuID, dimNONE);
}
/*
copy the menu so multiple instances of the same popup menu
don't conflict with each other.
*/
itsMenu = NIL;
theMenu = GetMenu( menuID);
CheckResource( (Handle) theMenu);
HandToHand(&theMenu);
err = MemError();
if (!gError->CheckOSError( err))
gError->SevereMacError( err);
/*
Note that we don't dispose of the original menu resource here, even
though we don't use it anymore. That's because the Bartender still
has a handle to it.
*/
HNoPurge( theMenu);
MoveHHi( theMenu);
itsMenu = theMenu;
/* setup runtime defaults */
UncheckAllItems();
enabled = TRUE;
wantsClicks = TRUE;
autoSelect = TRUE;
firstSelection = 0;
} /* CPopupMenu::IPopupMenuX */
/*****************************************************************************/
void CPopupMenu::Draw(Rect *area)
{
MenuHandle theMenu;
Int16 currItem, savedFont, savedSize, savedStyle;
Rect r, itemRect;
FontInfo fInfo;
Point loc;
PenState savedState;
if (!itsMenu) return;
theMenu = itsMenu;
r = frame;
if (!useWinFont)
{
savedFont = thePort->txFont;
savedSize = thePort->txSize;
savedStyle = thePort->txFace;
TextFace(0);
TextSize(12);
TextFont(0);
}
GetPenState(&savedState);
GetFontInfo(&fInfo);
MoveTo(1,fInfo.ascent+1);
HLock( theMenu);
DrawString((**theMenu).menuData);
HUnlock( theMenu);
GetPen( &loc);
loc.h += 5;
r.left = loc.h - 2; r.bottom--; r.right--;
EraseRect( &r);
/* setup rect for selected items and draw them */
itemRect.left = loc.h; itemRect.top = r.top +1;
itemRect.right = r.right -1; itemRect.bottom = r.bottom -1;
DrawSelectedItems( &itemRect, &fInfo);
/* draw box around current selection */
PenSize(1,1);
FrameRect(&r);
MoveTo(r.left + 1,r.bottom);
LineTo(r.right,r.bottom);
MoveTo(r.right,r.top + 1);
LineTo(r.right,r.bottom);
if (!enabled)
{
PenPat( gray);
PenMode(patBic);
r = frame;
PaintRect(&r);
}
SetPenState(&savedState);
if (!useWinFont)
{
TextFont( savedFont);
TextSize( savedSize);
TextFace( savedStyle);
}
} /* CPopupMenu::Draw */
/*****************************************************************************/
void CPopupMenu::DrawSelectedItems( Rect *itemRect, FontInfo *fInfo)
/*
draw the checked item(s). This implementation only draws the first
item, even if more than one is checked. Users most override to handle
multiple selections.
itemRect is the rect that this method must draw within. It has
already been erased.
fInfo is a FontInfo record for the font being used.
EllipseString is is imported from StringUtil.c
*/
{ Str255 s;
Int16 width, checkWidth;
if (firstSelection > 0)
{
/* get width available to draw current selection, take into
account space for checkmark */
if (useWinFont)
checkWidth = CharWidth( kAltCheckMark) + 4;
else checkWidth = CharWidth( checkMark);
width = itemRect->right - itemRect->left - checkWidth;
if (width <= 0) return;
GetItem( itsMenu, firstSelection, s);
EllipseString( s, width);
MoveTo( itemRect->left + checkWidth, itemRect->top + fInfo->ascent);
DrawString( s);
}
} /* DrawSelectedItems */
/*****************************************************************************/
void CPopupMenu::SelectItem( Int16 itemNum,tPMSelectAction actionType)
{
Rect r;
MenuHandle menu = itsMenu;
if ((itemNum <= 0)||(itemNum > CountMItems( menu))) return;
if (multiSelect)
{
if (actionType == pmToggle)
CheckMenuItem( itemNum, !ItemIsChecked( itemNum));
else CheckMenuItem( itemNum, actionType == pmForceOn);
firstSelection = GetCheckedItem();
}
else
{
/*
For single selection menus, we must modify the requested action to
ensure that there is always a selected item.
Note that we must ignore pmForceOff completely for this reason,
and pmForceOn accomplishes turning off the old selection and
turning on the new one.
*/
if ((actionType == pmForceOff)||(itemNum == firstSelection)) return;
CheckMenuItem( firstSelection, FALSE);
CheckMenuItem( itemNum, TRUE);
firstSelection = itemNum;
}
Refresh();
} /* CPopupMenu::SelectItem */
/*****************************************************************************/
void CPopupMenu::SelectItemName( StringPtr name, tPMSelectAction actionType)
/*
select item by name instead of menu item number.
*/
{ Int16 i, itemCount = CountMItems( itsMenu);
Str255 itemString;
for (i = 1; i <= itemCount; i++)
{
GetItem( itsMenu, i, itemString);
if (EqualString( name, itemString, FALSE, FALSE))
{
SelectItem( i, actionType);
break;
}
}
} /* CPopupMenu::SelectItemName */
/*****************************************************************************/
void CPopupMenu::DoClick(Point hitPt, Int16 modifierKeys, Int32 when)
{
register MenuHandle menu;
long menuResult;
Rect r,promptRect;
Int16 currItem,width,menuID, newItem;
Int16 savedFont, savedSize, savedStyle;
Int32 command;
char checkChar;
if (!itsMenu) return;
menu = itsMenu;
if (!useWinFont)
{
savedFont = thePort->txFont;
savedSize = thePort->txSize;
savedStyle = thePort->txFace;
TextFace(0);
TextSize(12);
TextFont(0);
checkChar = checkMark;
}
else checkChar = kAltCheckMark;
r = frame;
promptRect = r; promptRect.bottom --;
menuID = (**menu).menuID;
currItem = firstSelection; /* only 1st checked item if multiple */
HLock( menu);
width = StringWidth((**menu).menuData) + 5;
HUnlock( menu);
r.left += width; promptRect.right = r.left - 2;
LocalToGlobal(&topLeft(r));
InsertMenu(menu,-1);
CalcMenuSize(menu);
SetHiliteMode; InvertRect( &promptRect);
if (useWinFont)
{
menuFont = macPort->txFont;
realTextFont = NGetTrapAddress( _TextFont & 0x3FF, ToolTrap);
NSetTrapAddress( TextFontPatch, _TextFont & 0x3FF, ToolTrap);
}
menuResult = PopUpMenuSelect(menu, r.top + 1, r.left, currItem);
if (useWinFont)
{
NSetTrapAddress( realTextFont, _TextFont & 0x3FF, ToolTrap);
realTextFont = NIL;
}
SetHiliteMode; InvertRect( &promptRect);
DeleteMenu(menuID);
/*
Two separate things need to be done:
1) if autoSelect is active, we handle selecting a new item. We
must check that that (hiword(menuResult) == menuID) because
a submenu item might have been selected. We don't handle
automatically selecting submenu items.
2) If this is a command menu we see if the selected item
has a command. In this case, we do handle submenus,
i.e. submenus can have commands attached to menu items.
*/
newItem = loword( menuResult);
if ((autoSelect) && (hiword(menuResult) == menuID))
SelectItem( newItem, pmToggle);
menuID = hiword(menuResult); /* could be submenu */
if (isCommandMenu)
{
command = gBartender->FindCmdNumber( menuID, newItem);
/*
FindCmdNumber will return a positive integer if
there is a command for this item or a negative number
if the menu was registered but the item has no command.
If the menu was not registered it will return cmdNull;
*/
if (command != cmdNull)
itsSupervisor->DoCommand( command);
}
if (!useWinFont)
{
TextFont( savedFont);
TextSize( savedSize);
TextFace( savedStyle);
}
} /* CPopupMenu::DoClick */
/*****************************************************************************/
Boolean CPopupMenu::ItemIsChecked(Int16 item)
/*
return TRUE if item in menu has checkmark.
*/
{
Int16 markChar;
MenuHandle menu;
menu = itsMenu;
if (!menu) return FALSE;
GetItemMark(menu, item, &markChar);
return ((markChar == checkMark)||(markChar == kAltCheckMark));
} /* CPopupMenu::ItemIsChecked */
/*****************************************************************************/
Int16 CPopupMenu::GetCheckedItem()
/*
return item number if any item in menu
has a checkmark, else returns 0 (FALSE).
*/
{ Int16 i, count;
MenuHandle menu = itsMenu;
if (!menu) return 0;
count = CountMItems( menu);
for (i = 1; i <= count; i++)
{
if (ItemIsChecked( i)) return i;
}
return 0;
} /* CPopupMenu::GetCheckedItem */
/*****************************************************************************/
void CPopupMenu::UncheckAllItems( void)
{
Int16 i, count;
MenuHandle menu = itsMenu;
if (!itsMenu) return;
count = CountMItems( menu);
for (i = 1; i <= count; i++)
CheckMenuItem( i, FALSE);
firstSelection = 0;
} /* CPopupMenu::UncheckAllItems */
/*****************************************************************************/
void CPopupMenu::AppendItem( StringPtr newItem)
{
MenuHandle menu = itsMenu;
if (!itsMenu) return;
AppendMenu( menu, newItem);
} /* CPopupMenu::AppendItem */
/*****************************************************************************/
void CPopupMenu::InsertItem(StringPtr string, Int16 afterItem)
{
MenuHandle menu = itsMenu;
if (!itsMenu) return;
InsMenuItem( menu, string, afterItem);
} /* CPopupMenu::InsertItem */
/*****************************************************************************/
void CPopupMenu::DeleteItem( Int16 itemNum)
{
MenuHandle menu = itsMenu;
Rect r;
if (!itsMenu) return;
if ((itemNum > 0) && (itemNum <= CountMItems( menu)))
{
DelMenuItem( menu, itemNum);
if (itemNum == firstSelection)
{
firstSelection = GetCheckedItem();
if (macPort == thePort)
{
r = frame;
InvalRect( &r);
}
}
}
} /* CPopupMenu::DeleteItem */
/*****************************************************************************/
void CPopupMenu::DeleteAllItems()
{
MenuHandle menu = itsMenu;
Int16 i, count;
if (!itsMenu) return;
count = CountMItems( menu);
for ( i = count; i > 0; i--)
{
DelMenuItem( menu, i);
}
firstSelection = 0;
} /* CPopupMenu::DeleteAllItems */
/*****************************************************************************/
void CPopupMenu::SetItemString( Int16 item, StringPtr string)
{
if (!itsMenu) return;
SetItem( itsMenu, item, string);
if (ItemIsChecked( item)) Refresh();
} /* CPopupMenu::SetItemString */
/*****************************************************************************/
void CPopupMenu::SetCurrItemString( StringPtr string)
{
SetItemString( GetCheckedItem(), string);
} /* CPopupMenu::SetCurrItemString */
/*****************************************************************************/
void CPopupMenu::GetItemString( Int16 item, StringPtr string)
{
GetItem( itsMenu, item, string);
} /* CPopupMenu::GetItemString */
/*****************************************************************************/
void CPopupMenu::GetCurrItemString( StringPtr string)
{
GetItemString( GetCheckedItem(), string);
} /* CPopupMenu::GetCurrItemString */
/*****************************************************************************/
void CPopupMenu::SetAutoSelect( Int16 selectFlag)
{
autoSelect = selectFlag;
} /* CPopupMenu::SetAutoSelect */
/*****************************************************************************/
void CPopupMenu::CheckMenuItem( Int16 item, Int16 checkFlag)
{
Int16 cmdChar;
if (!itsMenu) return;
/* make sure that item does not have hierarchical menu,
otherwise clearing item mark would lose the submenu */
GetItemCmd( itsMenu, item, &cmdChar);
if (cmdChar == hMenuCmd) return;
if (checkFlag)
{
char checkChar = useWinFont? kAltCheckMark : checkMark;
SetItemMark( itsMenu, item, checkChar);
}
else SetItemMark( itsMenu, item, 0);
} /* CPopupMenu::CheckMenuItem */
/*****************************************************************************/
void CPopupMenu::SetEnable( Int16 enableFlag)
{
if (enableFlag != enabled)
{
enabled = enableFlag;
SetWantsClicks( enabled);
Refresh();
}
} /* CPopupMenu::SetEnable */
/*****************************************************************************/
Boolean CPopupMenu::GetEnable( void)
{
return enabled;
} /* CPopupMenu::GetEnable */
/*****************************************************************************/
void CPopupMenu::Dispose( void)
{
if (itsMenu) DisposeMenu( itsMenu);
} /* CPopupMenu::Dispose */
/*****************************************************************************/
void TextFontPatch( void)
{
asm {
move.l a5, a1 ; save current A5
move.l CurrentA5, a5 ; use app's globals
move.w menuFont, 4(sp) ; the desired font
move.l realTextFont, a0 ; address of _TextFont
move.l a5, a1 ; restore previous A5
jmp (a0) ; go to trap
}
}
/*****************************************************************************/